#define RUNTIME_DBG_LVL			0
#include <dbg/ptest.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <metalc/base/mtls.h>
#include <metalc/mlos/mmapheap.h>
#include <pthreadalt.h>

#include <unix/page_alloc_os.h>

#include "apds_common.h"


/* 定义mmap heap */
DEF_MMAPHEAP_MLAYER(mmap_normal, 2 * 1024 * 1024, SIZE_1G, SIZE_64G, 1, 0)

/* 定义buddy */
#include <metalc/mlayer/binbuddy.h>
static buddy_obj_t *pbd;
DEF_BUDDYEXT_MLAYER(buddy_ext, pbd, 16, HDEF_PTR_BITS, 21, page_alloc)

/* 组合 */
COMPOSE_MLAYER(test_buddy, buddy_ext, mmap_normal)

/* 定义tls */
DEF_MTLS_PTHREAD(GET_MLAYER_OBJNR(test_buddy), 4096, mlcc_palloc, mp_coreid)

/* 定义初始化过程 */
#include <pthreadalt/pthreadalt_init.h>
DEF_PTHREADALT_INIT
DEF_SYSINIT(mtls, testnode, test_buddy)
DEF_CFLOWFIN(mtls)
DEF_CFLOWINIT(mtls, test_buddy)
DEF_SYSFIN(__omega)

/* 测试 */

#define TEST_MAXTHREADS	(24)
#define TEST_NR		(100000)

#define TEST_MINSIZE	(4096)
#define TEST_MAXSIZE	(65536)
#define TEST_SET	(0)
#define TEST_ALLOC	test_buddy_alloc
#define TEST_FREE	test_buddy_free
#define TEST_SIZEOF	test_buddy_sizeof
static void *bench_buddy(void *args){
	PARAM_USED(args);
	mreq_t req;
	for (word_t idx = 0; idx < TEST_NR; idx++){
		size_t size = TEST_MINSIZE + hash(idx + rdcnt_relax()) % (TEST_MAXSIZE - TEST_MINSIZE);
		mreq_setalloc(&req, size, 0);
		void *mem = TEST_ALLOC(&req);
		assert(mem);
		size_t rsize = TEST_SIZEOF(&req);
		assert(rsize >= size);

		mem = req.ptr;
		PARAM_USED(mem);
		req.size = rsize;
		if (TEST_SET) __memset(mem, 0xff, size);

		bool_t s = TEST_FREE(&req);
		assert(s == TRUE);
	}
	return NULL;
}
PERF_PARALLEL_FUNC_THREADNR(bench_buddy, TEST_NR * 2, TEST_MAXTHREADS)

void test_buddy_basic(){
	mreq_t req;
	void *mem;
	mreq_setalloc(&req, 4096, 4096);
	mem = test_buddy_alloc(&req);
	assert(mem == req.ptr);
	// assert(mem);
	// assert(req.size == SIZE_2M); /* 等于对齐大小 */
	memset(mem, 0xff, 4096);
	// word_t *parr = (word_t *)mem;
	// assert(atm_ld(&(parr[256])) == (word_t)(-1));
	test_buddy_sizeof(&req);
	assert(req.size == 0x10000);
	assert(req.ptr == mem);
	bool_t s = test_buddy_free(&req);
	assert(s == TRUE);


	void *mem2, *mem3;
	mreq_setalloc(&req, 65536*2, 65536);
	mem = test_buddy_alloc(&req);

	mreq_setalloc(&req, 65536, 65536);
	mem2 = test_buddy_alloc(&req);

	mreq_setalloc(&req, 65536*2, 65536);
	mem3 = test_buddy_alloc(&req);

	req.ptr = mem2;
	test_buddy_sizeof(&req);
	assert(req.size == 0x10000);
	s = test_buddy_free(&req);
	assert(s == TRUE);

	req.ptr = mem;
	test_buddy_sizeof(&req);
	assert(req.size == 0x20000);
	s = test_buddy_free(&req);
	assert(s == TRUE);

	req.ptr = mem3;
	test_buddy_sizeof(&req);
	assert(req.size == 0x20000);
	s = test_buddy_free(&req);
	assert(s == TRUE);
}

/* perf测试 */
int main(){
	test_buddy_basic();
	PERF_PARALLEL_RUN(bench_buddy);
	return 0;
}